/*
 * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: MulanPSL-2.0
 */
#![allow(
    dead_code,
    non_camel_case_types,
    non_upper_case_globals,
    unused_assignments,
    unused_mut,
    unused_variables
)]
use crate::common::*;
extern "C" {
    fn umask(__mask: __mode_t) -> __mode_t;
    fn mkdir(__path: *const libc::c_char, __mode: __mode_t) -> libc::c_int;
    fn stat(path:*const libc::c_char,buf:*mut stat) -> libc::c_int;
    fn fstat(filedes:libc::c_int,buf:*mut stat) -> libc::c_int;
    fn snprintf(
        _: *mut libc::c_char,
        _: libc::c_ulong,
        _: *const libc::c_char,
        _: ...
    ) -> libc::c_int;
    fn asprintf(__ptr: *mut *mut libc::c_char, __fmt: *const libc::c_char, _: ...) -> libc::c_int;
    fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
    fn free(_: *mut libc::c_void);
    fn memcpy(_: *mut libc::c_void, _: *const libc::c_void, _: libc::c_ulong) -> *mut libc::c_void;
    fn memset(_: *mut libc::c_void, _: libc::c_int, _: libc::c_ulong) -> *mut libc::c_void;
    fn lseek(__fd: libc::c_int, __offset: __off_t, __whence: libc::c_int) -> __off_t;
    fn close(__fd: libc::c_int) -> libc::c_int;
    fn read(__fd: libc::c_int, __buf: *mut libc::c_void, __nbytes: size_t) -> ssize_t;
    fn write(__fd: libc::c_int, __buf: *const libc::c_void, __n: size_t) -> ssize_t;
    fn pread(
        __fd: libc::c_int,
        __buf: *mut libc::c_void,
        __nbytes: size_t,
        __offset: __off_t,
    ) -> ssize_t;
    fn pwrite(
        __fd: libc::c_int,
        __buf: *const libc::c_void,
        __n: size_t,
        __offset: __off_t,
    ) -> ssize_t;
    fn chown(__file: *const libc::c_char, __owner: __uid_t, __group: __gid_t) -> libc::c_int;
    fn getpid() -> __pid_t;
    fn getppid() -> __pid_t;
    fn unlink(__name: *const libc::c_char) -> libc::c_int;
    fn ftruncate(__fd: libc::c_int, __length: __off_t) -> libc::c_int;
    fn __errno_location() -> *mut libc::c_int;
    fn fcntl(__fd: libc::c_int, __cmd: libc::c_int, _: ...) -> libc::c_int;
    fn open(__file: *const libc::c_char, __oflag: libc::c_int, _: ...) -> libc::c_int;
    fn kill(__pid: __pid_t, __sig: libc::c_int) -> libc::c_int;
    fn sigemptyset(__set: *mut sigset_t) -> libc::c_int;
    fn sigaddset(__set: *mut sigset_t, __signo: libc::c_int) -> libc::c_int;
    fn sigprocmask(
        __how: libc::c_int,
        __set: *const sigset_t,
        __oset: *mut sigset_t,
    ) -> libc::c_int;
    fn sigaction(
        __sig: libc::c_int,
        __act: *const sigaction,
        __oact: *mut sigaction,
    ) -> libc::c_int;
    fn sudo_warn_gettext_v1(
        domainname: *const libc::c_char,
        msgid: *const libc::c_char,
    ) -> *mut libc::c_char;
    fn sudo_warn_nodebug_v1(fmt: *const libc::c_char, _: ...);
    fn sudo_warnx_nodebug_v1(fmt: *const libc::c_char, _: ...);
    static mut sudo_defs_table: [sudo_defs_types; 0];
    fn log_warning(flags: libc::c_int, fmt: *const libc::c_char, _: ...) -> bool;
    fn log_warningx(flags: libc::c_int, fmt: *const libc::c_char, _: ...) -> bool;
    fn sudo_gettime_mono_v1(ts: *mut timespec) -> libc::c_int;
    fn sudo_gettime_real_v1(ts: *mut timespec) -> libc::c_int;
    fn sudo_lock_region_v1(fd: libc::c_int, action: libc::c_int, len: off_t) -> bool;
    fn sudo_secure_dir_v1(
        path: *const libc::c_char,
        uid: uid_t,
        gid: gid_t,
        sbp: *mut stat,
    ) -> libc::c_int;
    fn sudo_debug_exit_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        subsys: libc::c_int,
    );
    fn sudo_debug_exit_bool_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        subsys: libc::c_int,
        ret: bool,
    );
    fn sudo_debug_exit_int_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        subsys: libc::c_int,
        ret: libc::c_int,
    );
    fn sudo_debug_exit_ptr_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        subsys: libc::c_int,
        ret: *const libc::c_void,
    );
    fn sudo_debug_exit_ssize_t_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        subsys: libc::c_int,
        ret: ssize_t,
    );
    fn sudo_debug_printf2_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        level: libc::c_int,
        fmt: *const libc::c_char,
        _: ...
    );
    static mut sudoers_subsystem_ids: [libc::c_uint; 0];
    fn get_boottime(_: *mut timespec) -> bool;
    fn restore_perms() -> bool;
    fn set_perms(_: libc::c_int) -> bool;
    static mut sudo_user: sudo_user;
    static mut timestamp_uid: uid_t;
    static mut timestamp_gid: gid_t;
    fn sudo_mkdir_parents(
        path: *mut libc::c_char,
        uid: uid_t,
        gid: gid_t,
        mode: mode_t,
        quiet: bool,
    ) -> bool;
    fn get_starttime(pid: pid_t, starttime: *mut timespec) -> libc::c_int;
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sigaction {
    pub __sigaction_handler: C2RustUnnamed_9,
    pub sa_mask: __sigset_t,
    pub sa_flags: libc::c_int,
    pub sa_restorer: Option<unsafe extern "C" fn() -> ()>,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub union C2RustUnnamed_9 {
    pub sa_handler: __sighandler_t,
    pub sa_sigaction:
        Option<unsafe extern "C" fn(libc::c_int, *mut siginfo_t, *mut libc::c_void) -> ()>,
}

pub const kernel: libc::c_uint = 9;
pub const tty: libc::c_uint = 8;
pub const ppid: libc::c_uint = 7;
pub const global: libc::c_uint = 6;
pub const digest_only: libc::c_uint = 5;
pub const all: libc::c_uint = 4;
pub const any: libc::c_uint = 3;
pub const always: libc::c_uint = 2;
pub const once: libc::c_uint = 1;
pub const never: libc::c_uint = 0;

pub const TS_GLOBAL: libc::c_int = 0x01;
pub const TS_TTY: libc::c_int = 0x02;
pub const TS_PPID: libc::c_int = 0x03;
pub const TS_LOCKEXCL: libc::c_int = 0x04;
pub const TS_DISABLED: libc::c_int = 0x01;
pub const TS_CURRENT: libc::c_int = 0;
pub const TS_OLD: libc::c_int = 1;
pub const TS_ERROR: libc::c_int = 3;
pub const SUDO_PATH_SECURE: libc::c_int = 0;
pub const SUDO_PATH_MISSING: libc::c_int = -1;
pub const SUDO_PATH_BAD_TYPE: libc::c_int = -2;
pub const SUDO_PATH_WRONG_OWNER: libc::c_int = -3;
pub const SUDO_PATH_WORLD_WRITABLE: libc::c_int = -4;
pub const SUDO_PATH_GROUP_WRITABLE: libc::c_int = -5;
pub const PERM_TIMESTAMP: libc::c_int = 0x06;
pub const SEEK_CUR: libc::c_int = 1;
pub const SLOG_SEND_MAIL: libc::c_int = 0x08;
pub const TS_VERSION: libc::c_ushort = 2;
pub const TS_ANYUID: libc::c_int = 0x02;
pub const TIMESTAMP_OPEN_ERROR: libc::c_int = -1;
pub const TIMESTAMP_PERM_ERROR: libc::c_int = -2;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct timestamp_entry {
    pub version: libc::c_ushort,
    pub size: libc::c_ushort,
    pub type_0: libc::c_ushort,
    pub flags: libc::c_ushort,
    pub auth_uid: uid_t,
    pub sid: pid_t,
    pub start_time: timespec,
    pub ts: timespec,
    pub u: C2RustUnnamed_11,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub union C2RustUnnamed_11 {
    pub ttydev: dev_t,
    pub ppid: pid_t,
}

/*
 * Each user has a single time stamp file that contains multiple records.
 * Records are locked to ensure that changes are serialized.
 *
 * The first record is of type TS_LOCKEXCL and is used to gain exclusive
 * access to create new records.  This is a short-term lock and sudo
 * should not sleep while holding it (or the user will not be able to sudo).
 * The TS_LOCKEXCL entry must be unlocked before locking the actual record.
 */
#[derive(Copy, Clone)]
#[repr(C)]
pub struct ts_cookie {
    pub fname: *mut libc::c_char,
    pub fd: libc::c_int,
    pub sid: pid_t,
    pub locked: bool,
    pub pos: off_t,
    pub key: timestamp_entry,
}

/*
 * Returns true if entry matches key, else false.
 * We don't match on the sid or actual time stamp.
 */
fn ts_match_record(
    mut key: *mut timestamp_entry,
    mut entry: *mut timestamp_entry,
    mut recno: libc::c_uint,
) -> bool {
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());
    }

    unsafe {
        if (*entry).version as libc::c_int != (*key).version as libc::c_int {
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG,
                b"%s:%u record version mismatch (want %u, got %u)\0" as *const u8
                    as *const libc::c_char,
                get_function_name!(),
                recno,
                (*key).version as libc::c_int,
                (*entry).version as libc::c_int
            );

            debug_return_bool!(false);
        }

        if ISSET!((*key).flags as libc::c_int, TS_ANYUID) == 0
            && (*entry).auth_uid != (*key).auth_uid
        {
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG,
                b"%s:%u record uid mismatch (want %u, got %u)\0" as *const u8
                    as *const libc::c_char,
                get_function_name!(),
                recno,
                (*key).auth_uid as libc::c_int,
                (*entry).auth_uid as libc::c_int
            );

            debug_return_bool!(false);
        }
        if (*entry).type_0 as libc::c_int != (*key).type_0 as libc::c_int {
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG,
                b"%s:%u record type mismatch (want %u, got %u)\0" as *const u8
                    as *const libc::c_char,
                get_function_name!(),
                recno,
                (*key).type_0 as libc::c_int,
                (*entry).type_0 as libc::c_int
            );
            debug_return_bool!(false);
        }

        match (*entry).type_0 as libc::c_int {
            TS_GLOBAL => { /* no ppid or tty to match */ }
            TS_PPID => {
                /* verify parent pid */
                if (*entry).u.ppid != (*key).u.ppid {
                    sudo_debug_printf!(
                        SUDO_DEBUG_DEBUG,
                        b"%s:%u record ppid mismatch (want %d, got %d)\0" as *const u8
                            as *const libc::c_char,
                        get_function_name!(),
                        recno,
                        (*key).u.ppid,
                        (*entry).u.ppid
                    );

                    debug_return_bool!(false);
                }
                if if (*entry).start_time.tv_sec == (*key).start_time.tv_sec {
                    ((*entry).start_time.tv_nsec != (*key).start_time.tv_nsec) as libc::c_int
                } else {
                    ((*entry).start_time.tv_sec != (*key).start_time.tv_sec) as libc::c_int
                } != 0
                {
                    sudo_debug_printf!(
                        SUDO_DEBUG_DEBUG,
                        b"%s:%u ppid start time mismatch\0" as *const u8 as *const libc::c_char,
                        get_function_name!(),
                        recno
                    );

                    debug_return_bool!(false);
                }
            }
            TS_TTY => {
                if (*entry).u.ttydev != (*key).u.ttydev {
                    sudo_debug_printf!(
                        SUDO_DEBUG_DEBUG,
                        b"%s:%u record tty mismatch (want 0x%x, got 0x%x)\0" as *const u8
                            as *const libc::c_char,
                        get_function_name!(),
                        recno,
                        (*key).u.ttydev,
                        (*entry).u.ttydev
                    );

                    debug_return_bool!(false);
                }
                if if (*entry).start_time.tv_sec == (*key).start_time.tv_sec {
                    ((*entry).start_time.tv_nsec != (*key).start_time.tv_nsec) as libc::c_int
                } else {
                    ((*entry).start_time.tv_sec != (*key).start_time.tv_sec) as libc::c_int
                } != 0
                {
                    sudo_debug_printf!(
                        SUDO_DEBUG_DEBUG,
                        b"%s:%u session leader start time mismatch\0" as *const u8
                            as *const libc::c_char,
                        get_function_name!(),
                        recno
                    );

                    debug_return_bool!(false);
                }
            }
            _ => {
                /* unknown record type, ignore it */
                sudo_debug_printf!(
                    SUDO_DEBUG_WARN | SUDO_DEBUG_LINENO,
                    b"%s:%u unknown time stamp record type %d\0" as *const u8
                        as *const libc::c_char,
                    get_function_name!(),
                    recno,
                    (*entry).type_0 as libc::c_int
                );

                debug_return_bool!(false);
            }
        }

        debug_return_bool!(true);
    } //unsafe
}

/*
 * Searches the time stamp file descriptor for a record that matches key.
 * On success, fills in entry with the matching record and returns true.
 * On failure, returns false.
 *
 * Note that records are searched starting at the current file offset,
 * which may not be the beginning of the file.
 */
fn ts_find_record(
    mut fd: libc::c_int,
    mut key: *mut timestamp_entry,
    mut entry: *mut timestamp_entry,
) -> bool {
    let mut cur: timestamp_entry = timestamp_entry {
        version: 0,
        size: 0,
        type_0: 0,
        flags: 0,
        auth_uid: 0,
        sid: 0,
        start_time: timespec {
            tv_sec: 0,
            tv_nsec: 0,
        },
        ts: timespec {
            tv_sec: 0,
            tv_nsec: 0,
        },
        u: C2RustUnnamed_11 { ttydev: 0 },
    };
    let mut recno: libc::c_uint = 0 as libc::c_int as libc::c_uint;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());
    }

    /*
     * Find a matching record (does not match sid or time stamp value).
     */
    unsafe {
        while read(
            fd,
            &mut cur as *mut timestamp_entry as *mut libc::c_void,
            ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong,
        ) as libc::c_ulong
            == ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong
        {
            recno = recno.wrapping_add(1);
            if cur.size as libc::c_ulong
                != ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong
            {
                /* wrong size, seek to start of next record */
                sudo_debug_printf!(
                    SUDO_DEBUG_INFO | SUDO_DEBUG_LINENO,
                    b"wrong sized record, got %hu, expected %zu\0" as *const u8
                        as *const libc::c_char,
                    cur.size as libc::c_int,
                    ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong
                );

                if lseek(
                    fd,
                    cur.size as off_t
                        - ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as off_t,
                    1 as libc::c_int,
                ) == -(1 as libc::c_int) as libc::c_long
                {
                    sudo_debug_printf!(
                        SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO | SUDO_DEBUG_LINENO,
                        b"unable to seek forward %d\0" as *const u8 as *const libc::c_char,
                        cur.size as libc::c_int
                            - ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong
                                as libc::c_int
                    );

                    break;
                }
                if cur.size as libc::c_int == 0 as libc::c_int {
                    break; /* size must be non-zero */
                }
                continue;
            }

            if ts_match_record(key, &mut cur, recno) {
                memcpy(
                    entry as *mut libc::c_void,
                    &mut cur as *mut timestamp_entry as *const libc::c_void,
                    ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong,
                );
                debug_return_bool!(true);
            }
        }

        debug_return_bool!(false);
    }
}

/*
 * Create a directory and any missing parent directories with the
 * specified mode.
 * Returns true on success.
 * Returns false on failure and displays a warning to stderr.
 */
fn ts_mkdirs(
    mut path: *mut libc::c_char,
    mut owner: uid_t,
    mut group: gid_t,
    mut mode: mode_t,
    mut parent_mode: mode_t,
    mut quiet: bool,
) -> bool {
    let mut ret: bool = false;
    let mut omask: mode_t = 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());

        /* umask must not be more restrictive than the file modes. */
        omask = umask(ACCESSPERMS!() as libc::c_uint & !(mode | parent_mode));

        ret = sudo_mkdir_parents(path, owner, group, parent_mode, quiet);
        if ret {
            /* Create final path component. */
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"mkdir %s, mode 0%o, uid %d, gid %d\0" as *const u8 as *const libc::c_char,
                path,
                mode,
                owner as libc::c_int,
                group as libc::c_int
            );

            if mkdir(path, mode) != 0 as libc::c_int && *__errno_location() != EEXIST {
                if !quiet {
                    sudo_warn!(
                        b"unable to mkdir %s\0" as *const u8 as *const libc::c_char,
                        path
                    );
                }
                ret = false;
            } else if chown(path, owner, group) != 0 as libc::c_int {
                sudo_debug_printf!(
                    SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                    b"%s: unable to chown %d:%d %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    owner as libc::c_int,
                    group as libc::c_int,
                    path
                );
            }

            /*} else {
                if chown(path, owner, group) != 0 as libc::c_int {
                    sudo_debug_printf!(
                        SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO,
                        b"%s: unable to chown %d:%d %s\0" as *const u8 as *const libc::c_char,
                        get_function_name!(),
                        owner as libc::c_int,
                        group as libc::c_int,
                        path
                    );
                }
            }*/
        }
        umask(omask);
        debug_return_bool!(ret);
    } //unsafe
}

/*
 * Check that path is owned by timestamp_uid and not writable by
 * group or other.  If path is missing and make_it is true, create
 * the directory and its parent dirs.
 * Returns true on success or false on failure, setting errno.
 */
fn ts_secure_dir(mut path: *mut libc::c_char, mut make_it: bool, mut quiet: bool) -> bool {
    let mut sb: stat = sb_all_arch;
    let mut ret: bool = 0 as libc::c_int != 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());
    }

    unsafe {
        sudo_debug_printf!(
            SUDO_DEBUG_INFO | SUDO_DEBUG_LINENO,
            b"checking %s\0" as *const u8 as *const libc::c_char,
            path
        );
    }

    unsafe {
        match sudo_secure_dir_v1(path, timestamp_uid, -(1 as libc::c_int) as gid_t, &mut sb) {
            SUDO_PATH_SECURE => {
                ret = true;
            }
            SUDO_PATH_MISSING => {
                if make_it as libc::c_int != 0
                    && ts_mkdirs(
                        path,
                        timestamp_uid,
                        timestamp_gid,
                        S_IRWXU as mode_t,
                        (S_IRWXU | S_IXGRP | S_IXOTH) as mode_t,
                        quiet,
                    ) as libc::c_int
                        != 0
                {
                    ret = true;
                } else {
                    *__errno_location() = ENOENT;
                }
            }
            SUDO_PATH_BAD_TYPE => {
                *__errno_location() = ENOTDIR;
                if !quiet {
                    sudo_warn!(b"%s\0" as *const u8 as *const libc::c_char, path);
                }
            }
            SUDO_PATH_WRONG_OWNER => {
                if !quiet {
                    sudo_warnx!(
                        b"%s is owned by uid %u, should be %u\0" as *const u8
                            as *const libc::c_char,
                        path,
                        sb.st_uid,
                        timestamp_uid
                    );
                }
                *__errno_location() = EACCES;
            }
            SUDO_PATH_GROUP_WRITABLE => {
                if !quiet {
                    sudo_warnx!(
                        b"%s is group writable\0" as *const u8 as *const libc::c_char,
                        path
                    );
                }
                *__errno_location() = EACCES;
            }
            _ => {}
        }
        debug_return_bool!(ret);
    }
}

/*
 * Open the specified timestamp or lecture file and set the
 * close on exec flag.
 * Returns open file descriptor on success.
 * Returns TIMESTAMP_OPEN_ERROR or TIMESTAMP_PERM_ERROR on error.
 */
fn ts_open(mut path: *const libc::c_char, mut flags: libc::c_int) -> libc::c_int {
    let mut uid_changed: bool = false;
    let mut fd: libc::c_int = 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());

        if timestamp_uid != 0 {
            uid_changed = set_perms(PERM_TIMESTAMP as libc::c_int);
        }

        fd = open(path, flags, S_IRUSR | S_IWUSR!());
        if uid_changed as libc::c_int != 0 && !restore_perms() {
            /* Unable to restore permissions, should not happen. */
            if fd != -(1 as libc::c_int) {
                let mut serrno: libc::c_int = *__errno_location();
                close(fd);
                *__errno_location() = serrno;
                fd = TIMESTAMP_PERM_ERROR;
            }
        }
        if fd >= 0 as libc::c_int {
            fcntl(fd, F_SETFD, FD_CLOEXEC);
        }
    } //unsafe

    unsafe {
        debug_return_int!(fd);
    }
}

fn ts_write(
    mut fd: libc::c_int,
    mut fname: *const libc::c_char,
    mut entry: *mut timestamp_entry,
    mut offset: off_t,
) -> ssize_t {
    let mut nwritten: ssize_t = 0;
    let mut old_eof: off_t = 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());
    }

    unsafe {
        if offset == -(1 as libc::c_int) as libc::c_long {
            old_eof = lseek(fd, 0 as libc::c_int as __off_t, SEEK_CUR);
            nwritten = write(fd, entry as *const libc::c_void, (*entry).size as size_t);
        } else {
            old_eof = offset;
            nwritten = pwrite(
                fd,
                entry as *const libc::c_void,
                (*entry).size as size_t,
                offset,
            );
        }
        if nwritten as size_t != (*entry).size as libc::c_ulong {
            if nwritten == -(1 as libc::c_int) as libc::c_long {
                log_warning(
                    SLOG_SEND_MAIL,
                    b"unable to write to %s\0" as *const u8 as *const libc::c_char,
                    fname,
                );
            } else {
                log_warningx(
                    SLOG_SEND_MAIL,
                    b"unable to write to %s\0" as *const u8 as *const libc::c_char,
                    fname,
                );
            }

            /* Truncate on partial write to be safe (assumes end of file). */
            if nwritten > 0 as libc::c_int as libc::c_long {
                sudo_debug_printf!(
                    SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                    b"short write, truncating partial time stamp record\0" as *const u8
                        as *const libc::c_char
                );

                if ftruncate(fd, old_eof) != 0 as libc::c_int {
                    sudo_warn!(
                        b"unable to truncate time stamp file to %lld bytes\0" as *const u8
                            as *const libc::c_char,
                        old_eof as libc::c_longlong
                    );
                }
            }
            debug_return_ssize_t!(-1);
        }
        debug_return_ssize_t!(nwritten);
    } //unsafe
}

/*
 * Full in struct timestamp_entry with the specified flags
 * based on auth user pw.  Does not set the time stamp.
 */
fn ts_init_key(
    mut entry: *mut timestamp_entry,
    mut pw: *mut passwd,
    mut flags: libc::c_int,
    mut ticket_type: libc::c_uint,
) {
    let mut sb: stat = sb_all_arch;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());
    }

    unsafe {
        memset(
            entry as *mut libc::c_void,
            0 as libc::c_int,
            ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong,
        );
        (*entry).version = TS_VERSION;
        (*entry).size =
            ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as libc::c_ushort;
        (*entry).flags = flags as libc::c_ushort;
        if !pw.is_null() {
            (*entry).auth_uid = (*pw).pw_uid;
        } else {
            (*entry).flags = ((*entry).flags as libc::c_int | TS_ANYUID) as libc::c_ushort;
        }
        (*entry).sid = sudo_user.sid;
        match ticket_type as libc::c_uint {
            tty => {
                if !(sudo_user.ttypath).is_null()
                    && stat(sudo_user.ttypath, &mut sb) == 0 as libc::c_int
                {
                    /* tty-based time stamp */
                    (*entry).type_0 = TS_TTY as libc::c_ushort;
                    (*entry).u.ttydev = sb.st_rdev;
                    if (*entry).sid != -(1 as libc::c_int) {
                        get_starttime((*entry).sid, &mut (*entry).start_time);
                    }
                } else {
                    /* ppid-based time stamp */
                    (*entry).type_0 = TS_PPID as libc::c_ushort;
                    (*entry).u.ppid = getppid();
                    get_starttime((*entry).u.ppid, &mut (*entry).start_time);
                }
            }
            kernel | ppid => {
                /* ppid-based time stamp */
                (*entry).type_0 = TS_PPID as libc::c_ushort;
                (*entry).u.ppid = getppid();
                get_starttime((*entry).u.ppid, &mut (*entry).start_time);
            }
            global => {
                /* global time stamp */
                (*entry).type_0 = TS_GLOBAL as libc::c_ushort;
            }
            _ => {
                /* Unknown time stamp ticket type, treat as tty (should not happen). */
                sudo_warnx!(
                    b"unknown time stamp ticket type %d\0" as *const u8 as *const libc::c_char,
                    ticket_type as libc::c_uint
                );
            }
        }
        debug_return!();
    }
}
unsafe fn ts_init_key_nonglobal(
    mut entry: *mut timestamp_entry,
    mut pw: *mut passwd,
    mut flags: libc::c_int,
) {
    /*
     * Even if the timestamp type is global or kernel we still want to do
     * per-tty or per-ppid locking so sudo works predictably in a pipeline.
     */
    ts_init_key(
        entry,
        pw,
        flags,
        (if (*sudo_defs_table
            .as_mut_ptr()
            .offset(111 as libc::c_int as isize))
        .sd_un
        .tuple as libc::c_uint
            == ppid as libc::c_int as libc::c_uint
        {
            ppid as libc::c_int
        } else {
            tty as libc::c_int
        }) as libc::c_uint,
    );
}

/*
 * Open the user's time stamp file.
 * Returns a cookie or NULL on error, does not lock the file.
 */
#[no_mangle]
pub fn timestamp_open(mut user: *const libc::c_char, mut sid: pid_t) -> *mut libc::c_void {
    let mut cookie: *mut ts_cookie = std::ptr::null_mut::<ts_cookie>();
    let mut fname: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut tries: libc::c_int = 0;
    let mut fd: libc::c_int = -(1 as libc::c_int);
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());

        /* Zero timeout means don't use the time stamp file. */
        if !((*sudo_defs_table
            .as_mut_ptr()
            .offset(34 as libc::c_int as isize))
        .sd_un
        .tspec
        .tv_sec
            != 0
            || (*sudo_defs_table
                .as_mut_ptr()
                .offset(34 as libc::c_int as isize))
            .sd_un
            .tspec
            .tv_nsec
                != 0)
        {
            *__errno_location() = ENOENT;

            // goto bad
            if fd != -(1 as libc::c_int) {
                close(fd);
            }
            free(fname as *mut libc::c_void);
            debug_return_ptr!(std::ptr::null_mut::<libc::c_void>());
        }

        /* Sanity check timestamp dir and create if missing. */
        if !ts_secure_dir(
            (*sudo_defs_table
                .as_mut_ptr()
                .offset(46 as libc::c_int as isize))
            .sd_un
            .str_0,
            true,
            false,
        ) {
            // goto bad
            if fd != -(1 as libc::c_int) {
                close(fd);
            }
            free(fname as *mut libc::c_void);
            debug_return_ptr!(std::ptr::null_mut::<libc::c_void>());
        }

        /* Open time stamp file. */
        if asprintf(
            &mut fname as *mut *mut libc::c_char,
            b"%s/%s\0" as *const u8 as *const libc::c_char,
            (*sudo_defs_table
                .as_mut_ptr()
                .offset(46 as libc::c_int as isize))
            .sd_un
            .str_0,
            user,
        ) == -(1 as libc::c_int)
        {
            sudo_warnx!(
                b"%s: %s\0" as *const u8 as *const libc::c_char,
                get_function_name!(),
                b"unable to allocate memory\0" as *const u8 as *const libc::c_char
            );

            // goto bad
            if fd != -(1 as libc::c_int) {
                close(fd);
            }
            free(fname as *mut libc::c_void);
            debug_return_ptr!(std::ptr::null_mut::<libc::c_void>());
        }

        tries = 1 as libc::c_int;
        loop {
            let mut sb: stat = sb_all_arch;
            fd = ts_open(fname, O_RDWR | O_CREAT);
            match fd {
                TIMESTAMP_OPEN_ERROR => {
                    log_warning(
                        0x8 as libc::c_int,
                        b"unable to open %s\0" as *const u8 as *const libc::c_char,
                        fname,
                    );
                    // goto bad
                    if fd != -(1 as libc::c_int) {
                        close(fd);
                    }
                    free(fname as *mut libc::c_void);
                    debug_return_ptr!(std::ptr::null_mut::<libc::c_void>());
                }
                TIMESTAMP_PERM_ERROR => {
                    /* Already logged set_perms/restore_perms error. */
                    // goto bad
                    if fd != -(1 as libc::c_int) {
                        close(fd);
                    }
                    free(fname as *mut libc::c_void);
                    debug_return_ptr!(std::ptr::null_mut::<libc::c_void>());
                }
                _ => {}
            }

            /* Remove time stamp file if its mtime predates boot time. */
            if tries == 1 as libc::c_int && fstat(fd, &mut sb) == 0 as libc::c_int {
                let mut boottime: timespec = timespec {
                    tv_sec: 0,
                    tv_nsec: 0,
                };
                let mut mtime: timespec = timespec {
                    tv_sec: 0,
                    tv_nsec: 0,
                };
                let mut now: timespec = timespec {
                    tv_sec: 0,
                    tv_nsec: 0,
                };
                if sudo_gettime_real_v1(&mut now) == 0 as libc::c_int
                    && get_boottime(&mut boottime) as libc::c_int != 0
                {
                    /* Ignore a boot time that is in the future. */
                    if if now.tv_sec == boottime.tv_sec {
                        (now.tv_nsec < boottime.tv_nsec) as libc::c_int
                    } else {
                        (now.tv_sec < boottime.tv_sec) as libc::c_int
                    } != 0
                    {
                        sudo_debug_printf!(
                            SUDO_DEBUG_WARN | SUDO_DEBUG_LINENO,
                            b"ignoring boot time that is in the future\0" as *const u8
                                as *const libc::c_char
                        );
                    } else {
                        mtime.tv_sec = sb.st_mtim.tv_sec;
                        mtime.tv_nsec = sb.st_mtim.tv_nsec;
                        if if mtime.tv_sec == boottime.tv_sec {
                            (mtime.tv_nsec < boottime.tv_nsec) as libc::c_int
                        } else {
                            (mtime.tv_sec < boottime.tv_sec) as libc::c_int
                        } != 0
                        {
                            /* Time stamp file too old, remove it. */
                            sudo_debug_printf!(
                                SUDO_DEBUG_WARN | SUDO_DEBUG_LINENO,
                                b"removing time stamp file that predates boot time\0" as *const u8
                                    as *const libc::c_char
                            );

                            close(fd);
                            unlink(fname);
                            tries += 1;
                            continue;
                        }
                    }
                }
            }
            break;
        }

        /* Allocate and fill in cookie to store state. */
        cookie = malloc(::core::mem::size_of::<ts_cookie>() as libc::c_ulong) as *mut ts_cookie;
        if cookie.is_null() {
            sudo_warnx!(
                b"%s: %s\0" as *const u8 as *const libc::c_char,
                get_function_name!(),
                b"unable to allocate memory\0" as *const u8 as *const libc::c_char,
            );

            // goto bad
            if fd != -(1 as libc::c_int) {
                close(fd);
            }
            free(fname as *mut libc::c_void);
            debug_return_ptr!(std::ptr::null_mut::<libc::c_void>());
        }

        (*cookie).fd = fd;
        (*cookie).fname = fname;
        (*cookie).sid = sid;
        (*cookie).pos = -(1 as libc::c_int) as off_t;

        debug_return_ptr!(cookie as *mut libc::c_void);
    } //unsafe
}

static mut got_signal: sig_atomic_t = 0;
unsafe extern "C" fn timestamp_handler(mut s: libc::c_int) {
    ::core::ptr::write_volatile(&mut got_signal as *mut sig_atomic_t, s);
}

/*
 * Wrapper for sudo_lock_region() that is interruptible.
 */
fn timestamp_lock_record(mut fd: libc::c_int, mut pos: off_t, mut len: off_t) -> bool {
    unsafe {
        let mut sa: sigaction = sigaction {
            __sigaction_handler: C2RustUnnamed_9 { sa_handler: None },
            sa_mask: __sigset_t { __val: [0; 16] },
            sa_flags: 0,
            sa_restorer: None,
        };
        let mut saveint: sigaction = sigaction {
            __sigaction_handler: C2RustUnnamed_9 { sa_handler: None },
            sa_mask: __sigset_t { __val: [0; 16] },
            sa_flags: 0,
            sa_restorer: None,
        };
        let mut savequit: sigaction = sigaction {
            __sigaction_handler: C2RustUnnamed_9 { sa_handler: None },
            sa_mask: __sigset_t { __val: [0; 16] },
            sa_flags: 0,
            sa_restorer: None,
        };
        let mut mask: sigset_t = __sigset_t { __val: [0; 16] };
        let mut omask: sigset_t = __sigset_t { __val: [0; 16] };
        let mut ret: bool = false;
        debug_decl!(SUDOERS_DEBUG_AUTH!());

        if pos >= 0 as libc::c_int as libc::c_long
            && lseek(fd, pos, 0 as libc::c_int) == -(1 as libc::c_int) as libc::c_long
        {
            sudo_debug_printf!(
                SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO | SUDO_DEBUG_LINENO,
                b"unable to seek to %lld\0" as *const u8 as *const libc::c_char,
                pos as libc::c_longlong
            );
            debug_return_bool!(false);
        }

        /* Allow SIGINT and SIGQUIT to interrupt a lock. */
        ::core::ptr::write_volatile(&mut got_signal as *mut sig_atomic_t, 0);
        memset(
            &mut sa as *mut sigaction as *mut libc::c_void,
            0 as libc::c_int,
            ::core::mem::size_of::<sigaction>() as libc::c_ulong,
        );
        sigemptyset(&mut sa.sa_mask);
        sa.sa_flags = 0 as libc::c_int; /* don't restart system calls */
        sa.__sigaction_handler.sa_handler =
            Some(timestamp_handler as unsafe extern "C" fn(libc::c_int) -> ());
        sigaction(SIGINT, &sa, &mut saveint);
        sigaction(SIGQUIT, &sa, &mut savequit);
        sigemptyset(&mut mask);
        sigaddset(&mut mask, SIGINT);
        sigaddset(&mut mask, SIGQUIT);
        sigprocmask(SIG_UNBLOCK, &mask, &mut omask);

        ret = sudo_lock_region_v1(fd, 1 as libc::c_int, len);
        if !ret {
            sudo_debug_printf!(
                SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO | SUDO_DEBUG_LINENO,
                b"failed to lock fd %d [%lld, %lld]\0" as *const u8 as *const libc::c_char,
                fd,
                pos as libc::c_longlong,
                len as libc::c_longlong
            );
        }

        /* Restore the old mask (SIGINT and SIGQUIT blocked) and handlers. */
        sigprocmask(SIG_SETMASK, &omask, std::ptr::null_mut::<sigset_t>());
        sigaction(SIGINT, &saveint, std::ptr::null_mut::<sigaction>());
        sigaction(SIGQUIT, &savequit, std::ptr::null_mut::<sigaction>());

        /* Re-deliver the signal that interrupted the lock, if any. */
        if !ret && got_signal != 0 {
            kill(getpid(), got_signal);
        }

        debug_return_bool!(ret);
    }
}

fn timestamp_unlock_record(mut fd: libc::c_int, mut pos: off_t, mut len: off_t) -> bool {
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());

        if pos >= 0 as libc::c_int as libc::c_long
            && lseek(fd, pos, SEEK_SET) == -(1 as libc::c_int) as libc::c_long
        {
            sudo_debug_printf!(
                SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO | SUDO_DEBUG_LINENO,
                b"unable to seek to %lld\0" as *const u8 as *const libc::c_char,
                pos as libc::c_longlong
            );

            debug_return_bool!(false);
        }
        debug_return_bool!(sudo_lock_region_v1(fd, SUDO_UNLOCK, len));
    }
}

/*
 * Seek to the record's position and read it, locking as needed.
 */
fn ts_read(mut cookie: *mut ts_cookie, mut entry: *mut timestamp_entry) -> ssize_t {
    let mut nread: ssize_t = -(1 as libc::c_int) as ssize_t;
    let mut should_unlock: bool = 0 as libc::c_int != 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());

        /* If the record is not already locked, lock it now.  */
        if !(*cookie).locked {
            if !timestamp_lock_record(
                (*cookie).fd,
                (*cookie).pos,
                ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as off_t,
            ) {
                // goto done
                if should_unlock {
                    timestamp_unlock_record(
                        (*cookie).fd,
                        (*cookie).pos,
                        ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as off_t,
                    );
                }
                debug_return_ssize_t!(nread);
            }
            should_unlock = true;
        }

        /* Seek to the record position and read it.  */
        nread = pread(
            (*cookie).fd,
            entry as *mut libc::c_void,
            ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong,
            (*cookie).pos,
        );
        if nread as libc::c_ulong != ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong {
            /* short read, should not happen */
            sudo_debug_printf!(
                SUDO_DEBUG_ERROR | SUDO_DEBUG_LINENO,
                b"short read (%zd vs %zu), truncated time stamp file?\0" as *const u8
                    as *const libc::c_char,
                nread,
                ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong
            );

            // goto done
            if should_unlock {
                timestamp_unlock_record(
                    (*cookie).fd,
                    (*cookie).pos,
                    ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as off_t,
                );
            }
            debug_return_ssize_t!(nread);
        }
        sudo_debug_printf!(
            SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
            b"read %zd byte record at %lld\0" as *const u8 as *const libc::c_char,
            nread,
            (*cookie).pos as libc::c_longlong
        );

        //done:
        if should_unlock {
            timestamp_unlock_record(
                (*cookie).fd,
                (*cookie).pos,
                ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as off_t,
            );
        }
        debug_return_ssize_t!(nread);
    }
}

/*
 * Lock a record in the time stamp file for exclusive access.
 * If the record does not exist, it is created (as disabled).
 */
#[no_mangle]
pub fn timestamp_lock(mut vcookie: *mut libc::c_void, mut pw: *mut passwd) -> bool {
    unsafe {
        let mut cookie: *mut ts_cookie = vcookie as *mut ts_cookie;
        let mut entry: timestamp_entry = timestamp_entry {
            version: 0,
            size: 0,
            type_0: 0,
            flags: 0,
            auth_uid: 0,
            sid: 0,
            start_time: timespec {
                tv_sec: 0,
                tv_nsec: 0,
            },
            ts: timespec {
                tv_sec: 0,
                tv_nsec: 0,
            },
            u: C2RustUnnamed_11 { ttydev: 0 },
        };
        let mut lock_pos: off_t = 0;
        let mut nread: ssize_t = 0;
        let sudo_debug_subsys_tmp: libc::c_int = *sudoers_subsystem_ids
            .as_mut_ptr()
            .offset(2 as libc::c_int as isize)
            as libc::c_int;

        debug_decl!(SUDOERS_DEBUG_AUTH!());
        if cookie.is_null() {
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"called with a NULL cookie!\0" as *const u8 as *const libc::c_char
            );
            debug_return_bool!(false);
        }

        /*
         * Take a lock on the "write" record (the first record in the file).
         * This will let us seek for the record or extend as needed
         * without colliding with anyone else.
         */

        if !timestamp_lock_record(
            (*cookie).fd,
            0 as libc::c_int as off_t,
            ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as off_t,
        ) {
            debug_return_bool!(false);
        }

        /* Make sure the first record is of type TS_LOCKEXCL. */
        memset(
            &mut entry as *mut timestamp_entry as *mut libc::c_void,
            0 as libc::c_int,
            ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong,
        );
        nread = read(
            (*cookie).fd,
            &mut entry as *mut timestamp_entry as *mut libc::c_void,
            ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong,
        );
        if nread == 0 as libc::c_int as libc::c_long {
            /* New file, add TS_LOCKEXCL record. */
            entry.version = TS_VERSION as libc::c_ushort;
            entry.size =
                ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as libc::c_ushort;
            entry.type_0 = TS_LOCKEXCL as libc::c_ushort;
            if ts_write(
                (*cookie).fd,
                (*cookie).fname,
                &mut entry,
                -(1 as libc::c_int) as off_t,
            ) == -(1 as libc::c_int) as libc::c_long
            {
                debug_return_bool!(false);
            }
        } else if entry.type_0 as libc::c_int != TS_LOCKEXCL {
            /* Old sudo record, convert it to TS_LOCKEXCL. */
            entry.type_0 = TS_LOCKEXCL as libc::c_ushort;
            memset(
                (&mut entry as *mut timestamp_entry as *mut libc::c_char)
                    .offset(6 as libc::c_ulong as isize) as *mut libc::c_void,
                0 as libc::c_int,
                (nread as libc::c_ulong).wrapping_sub(6 as libc::c_ulong),
            );
            if ts_write(
                (*cookie).fd,
                (*cookie).fname,
                &mut entry,
                0 as libc::c_int as off_t,
            ) == -(1 as libc::c_int) as libc::c_long
            {
                debug_return_bool!(false);
            }
        }
        if entry.size as libc::c_ulong != ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong
        {
            /* Reset position if the lock record has an unexpected size. */
            if lseek((*cookie).fd, entry.size as __off_t, SEEK_SET)
                == -(1 as libc::c_int) as libc::c_long
            {
                sudo_debug_printf!(
                    SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO | SUDO_DEBUG_LINENO,
                    b"unable to seek to %lld\0" as *const u8 as *const libc::c_char,
                    entry.size as libc::c_longlong
                );
                debug_return_bool!(false);
            }
        }

        /* Search for a tty/ppid-based record or append a new one. */
        sudo_debug_printf!(
            SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
            b"searching for %s time stamp record\0" as *const u8 as *const libc::c_char,
            if (*sudo_defs_table
                .as_mut_ptr()
                .offset(111 as libc::c_int as isize))
            .sd_un
            .tuple as libc::c_uint
                == ppid as libc::c_int as libc::c_uint
            {
                b"ppid\0" as *const u8 as *const libc::c_char
            } else {
                b"tty\0" as *const u8 as *const libc::c_char
            }
        );

        ts_init_key_nonglobal(&mut (*cookie).key, pw, TS_DISABLED);
        if ts_find_record((*cookie).fd, &mut (*cookie).key, &mut entry) {
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"found existing %s time stamp record\0" as *const u8 as *const libc::c_char,
                if (*sudo_defs_table
                    .as_mut_ptr()
                    .offset(111 as libc::c_int as isize))
                .sd_un
                .tuple as libc::c_uint
                    == ppid as libc::c_int as libc::c_uint
                {
                    b"ppid\0" as *const u8 as *const libc::c_char
                } else {
                    b"tty\0" as *const u8 as *const libc::c_char
                }
            );
            lock_pos = lseek((*cookie).fd, 0 as libc::c_int as __off_t, 1 as libc::c_int)
                - entry.size as off_t;
        } else {
            sudo_debug_printf2_v1(
                (*::core::mem::transmute::<&[u8; 15], &[libc::c_char; 15]>(b"timestamp_lock\0"))
                    .as_ptr(),
                b"timestamp.c\0" as *const u8 as *const libc::c_char,
                682 as libc::c_int,
                8 as libc::c_int | (1 as libc::c_int) << 5 as libc::c_int | sudo_debug_subsys_tmp,
                b"appending new %s time stamp record\0" as *const u8 as *const libc::c_char,
                if (*sudo_defs_table
                    .as_mut_ptr()
                    .offset(111 as libc::c_int as isize))
                .sd_un
                .tuple as libc::c_uint
                    == ppid as libc::c_int as libc::c_uint
                {
                    b"ppid\0" as *const u8 as *const libc::c_char
                } else {
                    b"tty\0" as *const u8 as *const libc::c_char
                },
            );

            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"appending new %s time stamp record\0" as *const u8 as *const libc::c_char,
                if (*sudo_defs_table
                    .as_mut_ptr()
                    .offset(111 as libc::c_int as isize))
                .sd_un
                .tuple as libc::c_uint
                    == ppid as libc::c_int as libc::c_uint
                {
                    b"ppid\0" as *const u8 as *const libc::c_char
                } else {
                    b"tty\0" as *const u8 as *const libc::c_char
                }
            );

            lock_pos = lseek((*cookie).fd, 0 as libc::c_int as __off_t, 1 as libc::c_int);
            if ts_write(
                (*cookie).fd,
                (*cookie).fname,
                &mut (*cookie).key,
                -(1 as libc::c_int) as off_t,
            ) == -(1 as libc::c_int) as libc::c_long
            {
                debug_return_bool!(false);
            }
        }

        sudo_debug_printf!(
            SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
            b"%s time stamp position is %lld\0" as *const u8 as *const libc::c_char,
            if (*sudo_defs_table
                .as_mut_ptr()
                .offset(111 as libc::c_int as isize))
            .sd_un
            .tuple as libc::c_uint
                == ppid as libc::c_int as libc::c_uint
            {
                b"ppid\0" as *const u8 as *const libc::c_char
            } else {
                b"tty\0" as *const u8 as *const libc::c_char
            },
            lock_pos as libc::c_longlong
        );

        if (*sudo_defs_table
            .as_mut_ptr()
            .offset(111 as libc::c_int as isize))
        .sd_un
        .tuple as libc::c_uint
            == global as libc::c_int as libc::c_uint
        {
            /*
             * For global tickets we use a separate record lock that we
             * cannot hold long-term since it is shared between all ttys.
             */
            (*cookie).locked = 0 as libc::c_int != 0;
            (*cookie).key.type_0 = TS_GLOBAL as libc::c_ushort; /* find a global record */

            if lseek((*cookie).fd, 0 as libc::c_int as __off_t, SEEK_SET)
                == -(1 as libc::c_int) as libc::c_long
            {
                sudo_debug_printf!(
                    SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO | SUDO_DEBUG_LINENO,
                    b"unable to rewind fd\0" as *const u8 as *const libc::c_char
                );
                debug_return_bool!(false);
            }
            if ts_find_record((*cookie).fd, &mut (*cookie).key, &mut entry) {
                sudo_debug_printf!(
                    SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                    b"found existing global record\0" as *const u8 as *const libc::c_char
                );

                (*cookie).pos = lseek((*cookie).fd, 0 as libc::c_int as __off_t, 1 as libc::c_int)
                    - entry.size as off_t;
            } else {
                sudo_debug_printf!(
                    SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                    b"appending new global record\0" as *const u8 as *const libc::c_char
                );

                (*cookie).pos = lseek((*cookie).fd, 0 as libc::c_int as __off_t, 1 as libc::c_int);
                if ts_write(
                    (*cookie).fd,
                    (*cookie).fname,
                    &mut (*cookie).key,
                    -(1 as libc::c_int) as off_t,
                ) == -(1 as libc::c_int) as libc::c_long
                {
                    debug_return_bool!(false);
                }
            }
        } else {
            /* For tty/ppid tickets the tty lock is the same as the record lock. */
            (*cookie).pos = lock_pos;
            (*cookie).locked = 1 as libc::c_int != 0;
        }

        /* Unlock the TS_LOCKEXCL record. */
        timestamp_unlock_record(
            (*cookie).fd,
            0 as libc::c_int as off_t,
            ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as off_t,
        );

        /* Lock the per-tty record (may sleep). */
        if !timestamp_lock_record(
            (*cookie).fd,
            lock_pos,
            ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as off_t,
        ) {
            debug_return_bool!(false);
        }
        debug_return_bool!(true);
    }
}

#[no_mangle]
pub fn timestamp_close(mut vcookie: *mut libc::c_void) {
    let mut cookie: *mut ts_cookie = vcookie as *mut ts_cookie;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());

        if !cookie.is_null() {
            close((*cookie).fd);
            free((*cookie).fname as *mut libc::c_void);
            free(cookie as *mut libc::c_void);
        }
        debug_return!();
    } //unsafe
}

/*
 * Check the time stamp file and directory and return their status.
 * Called with the file position before the locked record to read.
 * Returns one of TS_CURRENT, TS_OLD, TS_MISSING, TS_ERROR, TS_FATAL.
 * Fills in fdp with an open file descriptor positioned at the
 * appropriate (and locked) record.
 */
#[no_mangle]
pub fn timestamp_status(mut vcookie: *mut libc::c_void, mut pw: *mut passwd) -> libc::c_int {
    let mut cookie: *mut ts_cookie = vcookie as *mut ts_cookie;
    let mut entry: timestamp_entry = timestamp_entry {
        version: 0,
        size: 0,
        type_0: 0,
        flags: 0,
        auth_uid: 0,
        sid: 0,
        start_time: timespec {
            tv_sec: 0,
            tv_nsec: 0,
        },
        ts: timespec {
            tv_sec: 0,
            tv_nsec: 0,
        },
        u: C2RustUnnamed_11 { ttydev: 0 },
    };
    let mut diff: timespec = timespec {
        tv_sec: 0,
        tv_nsec: 0,
    };
    let mut now: timespec = timespec {
        tv_sec: 0,
        tv_nsec: 0,
    };
    let mut status: libc::c_int = TS_ERROR; /* assume the worst */
    let mut nread: ssize_t = 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());

        /* Zero timeout means don't use time stamp files. */
        if !((*sudo_defs_table
            .as_mut_ptr()
            .offset(34 as libc::c_int as isize))
        .sd_un
        .tspec
        .tv_sec
            != 0
            || (*sudo_defs_table
                .as_mut_ptr()
                .offset(34 as libc::c_int as isize))
            .sd_un
            .tspec
            .tv_nsec
                != 0)
        {
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"timestamps disabled\0" as *const u8 as *const libc::c_char
            );
            status = TS_OLD;

            //goto done;
            debug_return_int!(status);
        }

        if cookie.is_null() || (*cookie).pos < 0 as libc::c_int as libc::c_long {
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"NULL cookie or invalid position\0" as *const u8 as *const libc::c_char
            );
            status = TS_OLD;

            //goto done;
            debug_return_int!(status);
        }
    } //unsafe

    /* Read the record at the correct position. */
    nread = ts_read(cookie, &mut entry);
    if nread as libc::c_ulong != ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong {
        //goto done;
        unsafe {
            debug_return_int!(status);
        }
    }

    /* Make sure what we read matched the expected record. */
    if entry.version as libc::c_int != TS_VERSION as libc::c_int
        || entry.size as libc::c_long != nread
    {
        /* do something else? */
        unsafe {
            sudo_debug_printf!(
                SUDO_DEBUG_ERROR | SUDO_DEBUG_LINENO,
                b"invalid time stamp file @ %lld\0" as *const u8 as *const libc::c_char,
                (*cookie).pos as libc::c_longlong
            );
            status = TS_OLD;
            //goto done;
            debug_return_int!(status);
        }
    }

    // if entry.flags as libc::c_int & 0x1 as libc::c_int != 0
    if ISSET!(entry.flags as libc::c_int, TS_DISABLED) != 0 {
        unsafe {
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"time stamp record disabled\0" as *const u8 as *const libc::c_char
            );
        }

        status = TS_OLD; /* belongs to different session */
        //goto done;
        unsafe {
            debug_return_int!(status);
        }
    }

    unsafe {
        if entry.type_0 as libc::c_int != 0x1 as libc::c_int && entry.sid != (*cookie).sid {
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"time stamp record sid mismatch\0" as *const u8 as *const libc::c_char
            );
            status = TS_OLD; /* belongs to different session */
            // goto done;
            debug_return_int!(status);
        }

        /* Negative timeouts only expire manually (sudo -k).  */
        diff.tv_nsec = 0 as libc::c_int as __syscall_slong_t;

        diff.tv_sec = diff.tv_nsec;
        if if (*sudo_defs_table
            .as_mut_ptr()
            .offset(34 as libc::c_int as isize))
        .sd_un
        .tspec
        .tv_sec
            == diff.tv_sec
        {
            ((*sudo_defs_table
                .as_mut_ptr()
                .offset(34 as libc::c_int as isize))
            .sd_un
            .tspec
            .tv_nsec
                < diff.tv_nsec) as libc::c_int
        } else {
            ((*sudo_defs_table
                .as_mut_ptr()
                .offset(34 as libc::c_int as isize))
            .sd_un
            .tspec
            .tv_sec
                < diff.tv_sec) as libc::c_int
        } != 0
        {
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"time stamp record does not expire\0" as *const u8 as *const libc::c_char
            );
            status = TS_CURRENT;
            // goto done;
            debug_return_int!(status);
        }

        /* Compare stored time stamp with current time. */
        if sudo_gettime_mono_v1(&mut now) == -(1 as libc::c_int) {
            log_warning(
                0,
                b"unable to read the clock\0" as *const u8 as *const libc::c_char,
            );
            status = TS_ERROR;
            // goto done;
            debug_return_int!(status);
        }
        // sudo_timespecsub(&now, &entry.ts, &diff);
        diff.tv_sec = now.tv_sec - entry.ts.tv_sec;
        diff.tv_nsec = now.tv_nsec - entry.ts.tv_nsec;
        while diff.tv_nsec < 0 as libc::c_int as libc::c_long {
            diff.tv_sec -= 1;
            diff.tv_nsec += 1000000000 as libc::c_int as libc::c_long;
        }
        if if diff.tv_sec
            == (*sudo_defs_table
                .as_mut_ptr()
                .offset(34 as libc::c_int as isize))
            .sd_un
            .tspec
            .tv_sec
        {
            (diff.tv_nsec
                < (*sudo_defs_table
                    .as_mut_ptr()
                    .offset(34 as libc::c_int as isize))
                .sd_un
                .tspec
                .tv_nsec) as libc::c_int
        } else {
            (diff.tv_sec
                < (*sudo_defs_table
                    .as_mut_ptr()
                    .offset(34 as libc::c_int as isize))
                .sd_un
                .tspec
                .tv_sec) as libc::c_int
        } != 0
        {
            status = TS_CURRENT;
            if diff.tv_sec < 0 as libc::c_int as libc::c_long {
                log_warningx(
                    SLOG_SEND_MAIL,
                    b"ignoring time stamp from the future\0" as *const u8 as *const libc::c_char,
                );
                status = TS_OLD;
                entry.flags = (entry.flags as libc::c_int | 0x1 as libc::c_int) as libc::c_ushort;
                ts_write((*cookie).fd, (*cookie).fname, &mut entry, (*cookie).pos);
            }
        } else {
            status = TS_OLD;
        }

        debug_return_int!(status);
    } //unsafe
}

/*
 * Update the time on the time stamp file/dir or create it if necessary.
 * Returns true on success, false on failure or -1 on setuid failure.
 */
#[no_mangle]
pub fn timestamp_update(mut vcookie: *mut libc::c_void, mut pw: *mut passwd) -> bool {
    let mut cookie: *mut ts_cookie = vcookie as *mut ts_cookie;
    let mut ret: libc::c_int = 0 as libc::c_int;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());

        /* Zero timeout means don't use time stamp files. */
        if !((*sudo_defs_table
            .as_mut_ptr()
            .offset(34 as libc::c_int as isize))
        .sd_un
        .tspec
        .tv_sec
            != 0
            || (*sudo_defs_table
                .as_mut_ptr()
                .offset(34 as libc::c_int as isize))
            .sd_un
            .tspec
            .tv_nsec
                != 0)
        {
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"timestamps disabled\0" as *const u8 as *const libc::c_char
            );

            // goto done
            debug_return_bool!(ret != 0);
        }
        if cookie.is_null() || (*cookie).pos < 0 as libc::c_int as libc::c_long {
            sudo_debug_printf!(
                SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
                b"NULL cookie or invalid position\0" as *const u8 as *const libc::c_char
            );

            // goto done;
            debug_return_bool!(ret != 0);
        }
        /* Update timestamp in key and enable it. */
        // (*cookie).key.flags =
        //     ((*cookie).key.flags as libc::c_int & !(0x1 as libc::c_int)) as libc::c_ushort;
        CLR!((*cookie).key.flags, TS_DISABLED as libc::c_ushort);
        if sudo_gettime_mono_v1(&mut (*cookie).key.ts) == -(1 as libc::c_int) {
            log_warning(
                0 as libc::c_int,
                b"unable to read the clock\0" as *const u8 as *const libc::c_char,
            );
            // goto done;
            debug_return_bool!(ret != 0);
        }
        /* Write out the locked record. */
        sudo_debug_printf!(
            SUDO_DEBUG_DEBUG | SUDO_DEBUG_LINENO,
            b"writing %zu byte record at %lld\0" as *const u8 as *const libc::c_char,
            ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong,
            (*cookie).pos as libc::c_longlong
        );

        if ts_write(
            (*cookie).fd,
            (*cookie).fname,
            &mut (*cookie).key,
            (*cookie).pos,
        ) != -(1 as libc::c_int) as libc::c_long
        {
            ret = true as libc::c_int;
        }
        // done:
        debug_return_bool!(ret != 0);
    } //unsafe
}

/*
 * Remove the timestamp entry or file if unlink_it is set.
 * Returns true on success, false on failure or -1 on setuid failure.
 * A missing timestamp entry is not considered an error.
 */
#[no_mangle]
pub fn timestamp_remove(mut unlink_it: bool) -> libc::c_int {
    let mut key: timestamp_entry = timestamp_entry {
        version: 0,
        size: 0,
        type_0: 0,
        flags: 0,
        auth_uid: 0,
        sid: 0,
        start_time: timespec {
            tv_sec: 0,
            tv_nsec: 0,
        },
        ts: timespec {
            tv_sec: 0,
            tv_nsec: 0,
        },
        u: C2RustUnnamed_11 { ttydev: 0 },
    };
    let mut entry: timestamp_entry = timestamp_entry {
        version: 0,
        size: 0,
        type_0: 0,
        flags: 0,
        auth_uid: 0,
        sid: 0,
        start_time: timespec {
            tv_sec: 0,
            tv_nsec: 0,
        },
        ts: timespec {
            tv_sec: 0,
            tv_nsec: 0,
        },
        u: C2RustUnnamed_11 { ttydev: 0 },
    };
    let mut fd: libc::c_int = -(1 as libc::c_int);
    let mut ret: libc::c_int = 1 as libc::c_int;
    let mut fname: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());

        if asprintf(
            &mut fname as *mut *mut libc::c_char,
            b"%s/%s\0" as *const u8 as *const libc::c_char,
            def_timestampdir!(),
            sudo_user.name,
        ) == -(1 as libc::c_int)
        {
            sudo_warnx!(
                b"%s: %s\0" as *const u8 as *const libc::c_char,
                get_function_name!(),
                b"unable to allocate memory\0" as *const u8 as *const libc::c_char
            );

            ret = -(1 as libc::c_int);

            // goto done
            if fd != -(1 as libc::c_int) {
                close(fd);
            }
            free(fname as *mut libc::c_void);
            debug_return_int!(ret);
        }

        /* For "sudo -K" simply unlink the time stamp file. */
        if unlink_it {
            ret = if unlink(fname) != 0 {
                -(1 as libc::c_int)
            } else {
                true as libc::c_int
            };
        }

        /* Open time stamp file and lock it for exclusive access. */
        fd = ts_open(fname, O_RDWR);
        match fd {
            TIMESTAMP_OPEN_ERROR => {
                if *__errno_location() != ENOENT {
                    ret = false as libc::c_int;
                }
                // goto done
                if fd != -(1 as libc::c_int) {
                    close(fd);
                }
                free(fname as *mut libc::c_void);
                debug_return_int!(ret);
            }
            TIMESTAMP_PERM_ERROR => {
                /* Already logged set_perms/restore_perms error. */
                ret = -(1 as libc::c_int);

                // goto done
                if fd != -(1 as libc::c_int) {
                    close(fd);
                }
                free(fname as *mut libc::c_void);
                debug_return_int!(ret);
            }
            _ => {}
        }

        /* Lock first record to gain exclusive access. */
        if !(timestamp_lock_record(
            fd,
            -(1 as libc::c_int) as off_t,
            ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as off_t,
        )) {
            sudo_warn!(
                b"unable to lock time stamp file %s\0" as *const u8 as *const libc::c_char,
                fname
            );
            ret = -(1 as libc::c_int);

            // goto done
            if fd != -(1 as libc::c_int) {
                close(fd);
            }
            free(fname as *mut libc::c_void);
            debug_return_int!(ret);
        }
        /*
         * Find matching entries and invalidate them.
         */
        ts_init_key(
            &mut key,
            std::ptr::null_mut::<passwd>(),
            0 as libc::c_int,
            def_timestamp_type!() as libc::c_uint,
        );

        while ts_find_record(fd, &mut key, &mut entry) {
            /* Back up and disable the entry. */
            if ISSET!(entry.flags, TS_DISABLED as libc::c_ushort) == 0 {
                SET!(entry.flags, TS_DISABLED as libc::c_ushort);
                if lseek(
                    fd,
                    0 as libc::c_int as libc::c_long
                        - ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as off_t,
                    1 as libc::c_int,
                ) != -(1 as libc::c_int) as libc::c_long
                    && ts_write(fd, fname, &mut entry, -(1 as libc::c_int) as off_t)
                        == -(1 as libc::c_int) as libc::c_long
                {
                    ret = false as libc::c_int;
                }

                /*if lseek(
                    fd,
                    0 as libc::c_int as libc::c_long
                        - ::core::mem::size_of::<timestamp_entry>() as libc::c_ulong as off_t,
                    1 as libc::c_int,
                ) != -(1 as libc::c_int) as libc::c_long
                {
                    if ts_write(fd, fname, &mut entry, -(1 as libc::c_int) as off_t)
                        == -(1 as libc::c_int) as libc::c_long
                    {
                        ret = false as libc::c_int;
                    }
                }*/
            }
        }

        // done:
        if fd != -(1 as libc::c_int) {
            close(fd);
        }
        free(fname as *mut libc::c_void);
        debug_return_int!(ret);
    } //unsafe
}
#[no_mangle]
pub fn already_lectured(mut unused: libc::c_int) -> bool {
    let mut status_file: [libc::c_char; 4096] = [0; 4096];
    let mut sb: stat = sb_all_arch;
    let mut len: libc::c_int = 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());

        if ts_secure_dir(
            (*sudo_defs_table
                .as_mut_ptr()
                .offset(45 as libc::c_int as isize))
            .sd_un
            .str_0,
            false,
            true,
        ) {
            len = snprintf(
                status_file.as_mut_ptr(),
                ::core::mem::size_of::<[libc::c_char; 4096]>() as libc::c_ulong,
                b"%s/%s\0" as *const u8 as *const libc::c_char,
                def_lecture_status_dir!(),
                sudo_user.name,
            );
            if len > 0 as libc::c_int
                && (len as libc::c_long)
                    < ::core::mem::size_of::<[libc::c_char; 4096]>() as libc::c_ulong as ssize_t
            {
                debug_return_bool!(stat(status_file.as_mut_ptr(), &mut sb) == 0);
            }
            log_warningx(
                SLOG_SEND_MAIL,
                b"lecture status path too long: %s/%s\0" as *const u8 as *const libc::c_char,
                def_lecture_status_dir!(),
                sudo_user.name,
            );
        }
        debug_return_bool!(false);
    } //unsafe
}

/*
 * Create the lecture status file.
 * Returns true on success, false on failure or -1 on setuid failure.
 */
#[no_mangle]
pub fn set_lectured() -> libc::c_int {
    let mut lecture_status: [libc::c_char; 4096] = [0; 4096];
    let mut len: libc::c_int = 0;
    let mut fd: libc::c_int = 0;
    let mut ret: libc::c_int = 0 as libc::c_int;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_AUTH!());

        len = snprintf(
            lecture_status.as_mut_ptr(),
            ::core::mem::size_of::<[libc::c_char; 4096]>() as libc::c_ulong,
            b"%s/%s\0" as *const u8 as *const libc::c_char,
            def_lecture_status_dir!(),
            sudo_user.name,
        );
        if len < 0 as libc::c_int
            || len as libc::c_long
                >= ::core::mem::size_of::<[libc::c_char; 4096]>() as libc::c_ulong as ssize_t
        {
            log_warningx(
                SLOG_SEND_MAIL,
                b"lecture status path too long: %s/%s\0" as *const u8 as *const libc::c_char,
                def_lecture_status_dir!(),
                sudo_user.name,
            );
            // goto done;
            debug_return_int!(ret);
        }
        //  else
        /* Sanity check lecture dir and create if missing. */
        if !(ts_secure_dir(def_lecture_status_dir!(), true, false)) {
            // goto done;
            debug_return_int!(ret);
        }
        /* Create lecture file. */
        fd = ts_open(lecture_status.as_mut_ptr(), O_WRONLY | O_CREAT | O_EXCL);
        match fd {
            TIMESTAMP_OPEN_ERROR => { /* Failed to open, not a fatal error. */ }
            TIMESTAMP_PERM_ERROR => {
                /* Already logged set_perms/restore_perms error. */
                ret = -(1 as libc::c_int);
            }
            _ => {
                /* Success. */
                close(fd);
                ret = true as libc::c_int;
            }
        }

        debug_return_int!(ret);
    } //unsafe
}
